package echo

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"sync"
	"wx-uniapp-go/internal/domain/errcode"
	"wx-uniapp-go/runtime"
	"wx-uniapp-go/third_party/cdslog"
	reqloggin "wx-uniapp-go/third_party/cdsreq/gin"
	"wx-uniapp-go/third_party/errs"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
)

const (
	StatusOK   = 0
	StatusFail = 50000
)

var pool = &sync.Pool{
	New: func() interface{} {
		return &Echo{
			ctx:      nil,
			Response: &JSON{},
		}
	},
}

// ok 返回正确
var ok = &JSON{
	Code:    StatusOK,
	Message: "success",
}

// fail 返回错误
var fail = &JSON{
	Code:    StatusFail,
	Message: "error",
}

// Echo 对象
type Echo struct {
	ctx      *gin.Context
	Response *JSON
}

func OK(ctx *gin.Context) *Echo {
	echo := tryGet(ctx)
	*echo.Response = *ok
	return echo
}

func OkWithMsg(ctx *gin.Context, msg string, outputs ...interface{}) {
	echo := tryGet(ctx)
	echo.SetMessage(msg)
	if len(outputs) > 0 {
		echo.SetData(outputs[0])
	}
	echo.JSON()
}

func Success(ctx *gin.Context, outputs ...interface{}) {
	echo := tryGet(ctx)
	echo.SetMessage(ok.Message)
	if len(outputs) > 0 {
		echo.SetData(outputs[0])
	}
	echo.JSON()
}

func Download(ctx *gin.Context, data []byte, fileName string) {
	// 设置响应头，指定文件名和内容类型
	ctx.Header("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName))
	ctx.Header("Access-Control-Expose-Headers", "Content-Disposition")
	ctx.Data(http.StatusOK, "application/octet-stream", data)
}

func tryGet(ctx *gin.Context) *Echo {
	echo := pool.Get().(*Echo)
	echo.ctx = ctx
	return echo
}

func Fail(ctx *gin.Context) *Echo {
	echo := tryGet(ctx)
	*echo.Response = *fail
	return echo
}

func Error(ctx *gin.Context, err error) {
	echo := tryGet(ctx)
	*echo.Response = *fail
	echo.Error(err)
	ctx.Abort()
}

// SetData 设置消息体
func (e *Echo) SetData(data interface{}) *Echo {
	e.Response.Data = data
	return e
}

// SetMessage 设置消息内容
func (e *Echo) SetMessage(message string) *Echo {
	e.Response.Message = message
	return e
}

// SetCode 设置错误码
func (e *Echo) SetCode(code int) *Echo {
	e.Response.Code = code
	return e
}

func (e *Echo) DirectError(err error) {
	if runtime.RunMode == runtime.DEV {
		// 开发环境直接返回错误
		e.Error(err)
	} else {
		// 生产环境统一返回系统错误
		e.Error(errcode.SystemErr)
	}
}

// Error 处理错误响应
func (e *Echo) Error(err error, data ...interface{}) {
	var (
		code    = StatusFail
		message = "error"
	)

	// 根据环境决定是否显示详细错误
	if runtime.RunMode == runtime.DEV {
		// 开发环境：显示详细错误信息
		switch v := err.(type) {
		case *errs.Error:
			code = v.Code
			message = v.Error()
		case error:
			code = StatusFail
			message = v.Error()
		}
	} else {
		// 生产环境：统一显示系统错误
		switch v := err.(type) {
		case *errs.Error:
			if v.Code == errs.UnknownCode {
				// 未知错误统一显示系统错误
				code = errcode.SystemErr.Code
				message = errcode.SystemErr.Error()
			} else {
				// 业务错误保持原样
				code = v.Code
				message = v.Error()
			}
		default:
			// 非业务错误统一显示系统错误
			code = errcode.SystemErr.Code
			message = errcode.SystemErr.Error()
		}
	}

	e.Response.Code = code
	e.Response.Message = message

	// 如果提供了额外数据，设置到响应中
	if len(data) > 0 {
		e.Response.Data = data[0]
	}

	// 记录错误日志
	if runtime.RunMode == runtime.DEV {
		// 开发环境记录详细错误
		//cdslog.Error("response error",
		//	"code", code,
		//	"message", message,
		//	"error", err,
		//	"trace_id", e.Response.RequestId)
	}

	e.JSON()
}

// JSON 输出信息
func (e *Echo) JSON(customStatus ...int) {
	status := http.StatusOK
	if len(customStatus) > 0 {
		status = customStatus[0]
	}
	//requestId := e.ctx.GetString(runtime.TraceMark)
	requestId := reqloggin.GetTraceId(e.ctx)
	if requestId != "" {
		e.Response.RequestId = requestId
	}

	e.ctx.JSON(status, e.Response)
	// 添加请求日志
	// appId, _ := e.ctx.Get("app_id")
	// reqlog.SetAppid(e.ctx, cast.ToString(appId))
	// reqlog.SetUid(e.ctx, cast.ToString(session.GetUserID(e.ctx)))
	// 成功的
	if e.Response.Code == 0 {
		e.Response.Data = struct {
		}{}
	}
	rspcopy := *e.Response
	reqloggin.SetOutput(e.ctx, &rspcopy)
	e.release()
}

func (e *Echo) release() {
	e.Response.Data = nil
	e.Response.Code = 0
	e.Response.Message = ""
	pool.Put(e)
}

// JSONWith 携带data输出JSON
func (e *Echo) JSONWith(data interface{}, customStatus ...int) {
	e.SetData(data).JSON(customStatus...)
}

// ParamValidation parameter validation, return false means that the validation failed
func ParamValidation(ctx *gin.Context, obj interface{}) bool {
	err := ctx.ShouldBind(obj)
	if err == nil {
		return true
	}
	echo := tryGet(ctx)
	echo.Response.Message = getValidMsg(ctx, err, obj)
	echo.Response.Code = errcode.ParametersErr.Code
	echo.JSON()
	return false
}

// DirectBadRequest Directly return business-related errors.
func DirectBadRequest(ctx *gin.Context, format string, args ...any) {
	echo := tryGet(ctx)
	echo.Response.Message = fmt.Sprintf(format, args...)
	echo.Response.Code = errs.BadRequest
	echo.JSON()
}

// DirectBadRequestWithData Directly return business-related errors.
func DirectBadRequestWithData(ctx *gin.Context, data any, format string, args ...any) {
	echo := tryGet(ctx)
	echo.Response.Message = fmt.Sprintf(format, args...)
	echo.Response.Code = errs.BadRequest
	echo.Response.Data = data
	echo.JSON()
}

// DirectRespErr Respond directly with customize err
func DirectRespErr(ctx *gin.Context, err error) {
	echo := tryGet(ctx)
	echo.Response.Code = errcode.SystemErr.Code
	echo.Response.Message = errcode.SystemErr.Message
	var businessErr *errs.BusinessErr
	if errors.As(err, &businessErr) {
		echo.Response.Code = businessErr.Code
		echo.Response.Message = businessErr.Msg
		echo.Response.Data = businessErr.Data
	} else {
		cdslog.W(TraceCtx(ctx)).Error(err.Error())
	}
	echo.JSON()
}

// SSEventError 向流中发送错误消息
func SSEventError(ctx *gin.Context, err error) {
	var json JSON
	json.Code = errcode.SystemErr.Code
	json.Message = errcode.SystemErr.Message
	json.RequestId = reqloggin.GetTraceId(ctx)
	var businessErr *errs.BusinessErr
	if errors.As(err, &businessErr) {
		json.Code = businessErr.Code
		json.Message = businessErr.Msg
	} else {
		cdslog.W(TraceCtx(ctx)).Error(err.Error())
	}
	ctx.SSEvent("error", json)
}

// SSEventError 向流中发送终止消息
func SSEventDone(ctx *gin.Context) {
	ctx.SSEvent("done", "[[DONE]]")
}

// SSEventError 向流中发送消息
func SSEventData(ctx *gin.Context, data any) {
	var json JSON
	json.Code = errcode.OK.Code
	json.Message = errcode.OK.Message
	json.RequestId = reqloggin.GetTraceId(ctx)
	json.Data = data
	ctx.SSEvent("data", json)
}

// JSON JSON响应体
type JSON struct {
	RequestId string      `json:"trace_id,omitempty"`
	Code      int         `json:"code"`
	Message   string      `json:"msg"`
	Data      interface{} `json:"data,omitempty"`
}

func getValidMsg(ctx *gin.Context, err error, obj interface{}) string {
	if obj == nil {
		return err.Error()
	}
	if errs, ok := err.(validator.ValidationErrors); ok {
		getObj := reflect.TypeOf(obj)
		if getObj.Kind() == reflect.Ptr {
			getObj = getObj.Elem()
		}
		for _, e := range errs {
			if f, exist := getObj.FieldByName(e.Field()); exist {
				message := f.Tag.Get(e.Tag() + "Msg")
				if message == "" {
					message = f.Tag.Get("msg")
					if message == "" {
						return e.Error()
					}
				}
				return message
			}
		}
	}
	cdslog.W(TraceCtx(ctx)).Error(err.Error())
	return "参数无效"
}
